package Task17;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import Task17.Controller.DirectoryIsRootException;
import Task17.Controller.FileAlreadyExistException;
import Task17.Controller.MyFileNotFoundException;


public class Model {
	public static String workDir="C:\\";
	public static String command;
	public static String arg1;
	public static String arg2;
	public static int numArg;
	
	private boolean mapExist=false;  //map not create yet.
	private static Map <String, CommandInterface> commandMap;
	
	private void createMap(){
		commandMap = new HashMap<String, CommandInterface>();
		CommandInterface instance;
		for (Class<?> cl : new Model().getClass().getDeclaredClasses()) {         
			try {
				instance = (CommandInterface) cl.getConstructor(Model.class).newInstance(new Model());
				commandMap.put((String)instance.getClass().getDeclaredField("id").get(null),instance);
				
				
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (NoSuchFieldException e) {
				e.printStackTrace();
			} 
	          
	     }
		
		
	}
	
	public CommandInterface getObjectCommand(String id) throws NullPointerException{
		if(!mapExist){createMap(); mapExist=true;}
		CommandInterface myInst = commandMap.get(id);
		return myInst;
	}
	
	
	
	public class Dir extends CommandInterface{
		public  static final String id="dir";
		public boolean happerdError=false;
        
      public String getResult() {
			String printString="";
            
            File f=new File(arg1);
                    String[] dirList=f.list();
                int k=0;
                String[] fileList=new String[dirList.length];
                for(int i=0;i<dirList.length;i++){
                        File f1=new File(arg1+File.separator+dirList[i]);
                        if(!f1.isFile()) printString+="DIR "+dirList[i]+"\n";
                        else{
                                fileList[k]=dirList[i];
                                k++;
                        }
                }
                for(int i=0;i<k;i++){
                    printString+=fileList[i]+"\n";
                }
            return printString;
		}

      public String getError() {
    	  boolean bResult=true;
          String result="";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(numArg>1) {result+="Bad format of command\n"; bResult=false;}
          if(numArg==1) arg1=getAbsPath(workDir, arg1);
          if(numArg==0) arg1=workDir;
          if(!pathCheck(arg1)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result;   
		}
	}
	
	public class Tree extends CommandInterface{
		public static  final String id="tree";
		public boolean happerdError=false;
        
		private String printString;
        
        private void printNameDir(String directory,String dirList, int y){
                File f1=new File(directory+File.separator+dirList);
                if(!f1.isFile()){
                        for(int j=0; j<y;j++ ) printString+="  ";
                        printString+="+ "+dirList+"\n";
                        }
                else {
                        for(int j=0; j<y;j++ ) printString+="  "; 
                        printString+="  "+dirList+"\n";
                }
        }
                      
        private void insertedTree(String directory,int y){
                // y - level inserted. Start from 0. Increase.
                File f=new File(directory);
                String[] dirList=f.list();
                for(int i=0;i<dirList.length;i++){
                        printNameDir(directory,dirList[i],y);
                        // if dirList[i] is directory then call printTree for dirList[i]
                        String absDir=directory+File.separator+dirList[i];
                        File f1=new File(absDir);
                        if(!f1.isFile()) insertedTree(absDir,y+1);
                }
        }
		
		
      public String getResult() {
    	  printString=arg1+"\n";
          insertedTree(arg1,0);
          return printString;
      }

      public String getError() {
          boolean bResult=true;
          String result="";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(numArg>1) {result+="Bad format of command\n"; bResult=false;}
          if(numArg==1) arg1=getAbsPath(workDir, arg1);
          if(numArg==0) arg1=workDir;
          if(!pathCheck(workDir)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result; 
		}
    }

	public class Find extends CommandInterface{
		public static   final String id="find";
		public boolean happerdError=false;
		
		private String printString;
     
        private String searchInTreeRecursion(String directory, String mask){
                //Method runs searchInDir only in folder - 'dir' and run itself in all include folders
                File f=new File(directory);
                searchInDir(directory,mask);
                String[] dirList=f.list();
                for(int i=0;i<dirList.length;i++){
                        String absDir=directory+File.separator+dirList[i];
                        File f1=new File(absDir);
                        if(!f1.isFile()) searchInTreeRecursion(absDir,mask);
                        }
                return printString;
        }

        public void searchInDir(String directory, String mask){
                File f=new File(directory);
                String[] dirList=f.list();
                String[] arrayMask;
                
                if(mask.contains("*")) mask=mask.replace("*", "`");
                if(mask.contains("`")){
                                arrayMask = mask.split("`");
                
                for(int i=0;i<dirList.length;i++) {
                        String sDirList=dirList[i];
                        int k=0;
                        while(sDirList.length()>0 && k<arrayMask.length){
                                if(sDirList.startsWith(arrayMask[k])){
                                        for(int r=0; r<arrayMask[k].length();r++) sDirList=cutDir(sDirList);
                                        k++;
                                }
                                else{ 
                                        sDirList=cutDir(sDirList);
                                }
                        }
                        if(k>=arrayMask.length) printName(directory+File.separator+dirList[i]);
                }
                
                }
                else{
                        for(int i=0;i<dirList.length;i++) {
                                if(dirList[i].equals(mask))     printName(directory+File.separator+dirList[i]);
                        }
                }
                
                }

        private String cutDir(String directory){
                int ld=directory.length();
                char[] newDir=new char[ld-1];
                directory.getChars(1, ld, newDir, 0);
                String sNewDir=newDir.toString();
                sNewDir=sNewDir.copyValueOf(newDir);
                return sNewDir;
        }
        
        private void printName(String dirName){
                File f1=new File(dirName);
                Date date=new Date(f1.lastModified());
                printString+=f1.getName()+" \t"+date+" "+f1.getPath()+"\n";
        }
        
      public String getResult() {
    	  printString="";
          printString=searchInTreeRecursion(arg1, arg2);
          return printString;
		}

      public String getError() {
    	  boolean bResult=true;
    	  String result = "";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(!(numArg==2 || numArg==1)) {result+="Bad format of command\n"; bResult=false;}
          if(numArg==2) arg1=getAbsPath(workDir, arg1);  
          if(numArg==1) {arg2=arg1; arg1=workDir;}   
          if(!pathCheck(arg1)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result;
		}
	}
	
	public class Copy extends CommandInterface{
		public static  final String id="copy";
		public boolean happendError=false;
		
      public String getResult() throws FileNotFoundException, FileAlreadyExistException, IOException {
    	  boolean result=false;
    	  String sResult="Operation complete.";
    	  String sFileFrom=arg1;
    	  String sFileTo=arg2;
          
          File fileFrom=new File(sFileFrom);
                         
                          File fileTo=new File(sFileTo);
                          if(fileTo.exists()) throw new FileAlreadyExistException();
                          else{
                          InputStream in = new FileInputStream(fileFrom);
                          OutputStream out = new FileOutputStream(fileTo);
                          	byte[] buf = new byte[1024];
                            int len;
                            while ((len = in.read(buf)) > 0){
                            out.write(buf, 0, len);
                            }
                            in.close();
                            out.close();
                            sResult="File copied.";
                            result= true;
                          }
          happendError=!result;
          return sResult;
		}

      public String getError() {
    	  boolean bResult=true;
          String result="";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(numArg!=2) {result+="Bad format of command\n"; bResult=false;} 
          	else {arg1=getAbsPath(workDir, arg1); arg2=getAbsPath(workDir, arg2); }
          if(!pathCheck(arg1)) {result+="File not found\n"; bResult=false;}
          if(!pathCheck(workDir)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result;
          }
	}
	
	public class Move extends CommandInterface{
		public static  final String id="move";
		public boolean happendError=false;
        
      public String getResult() throws FileNotFoundException, FileAlreadyExistException, IOException {
    	  String sResult;
    	  boolean bResult=false;
    	  String fileFrom=arg1;
    	  String fileTo=arg2;
    	  Copy myCopy=new Copy();
    	  
    	  String arg2Copy=arg2;
    	  sResult=myCopy.getError();	//Change arg2 path, because it is not exist !!! We should save it! 
    	  arg2=arg2Copy;
    	  if(!((CommandInterface)myCopy).happendError) sResult=myCopy.getResult();
    	  
          if(!((CommandInterface)myCopy).happendError){
                  Del myDel=new Del();
                  if(myDel.delFile(fileFrom)) {bResult=true; sResult+=" Old file deleted."; }
          }
          happendError=!bResult;
          return sResult;
      }

      public String getError() {
    	  boolean bResult=true;
          String result="";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(numArg!=2) {result+="Bad format of command\n"; bResult=false;} 
          	else {arg1=getAbsPath(workDir, arg1); arg2=getAbsPath(workDir, arg2); }
          if(!pathCheck(arg1)) {result+="File not found\n"; bResult=false;}
          if(!pathCheck(workDir)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result;
      }
	}
	
	public class Del extends CommandInterface{
		public static  final String id="del";
		public boolean happerdError=false;
        
		public boolean delFile(String directory){
			boolean bResult=false;
            File delDir=new File(directory);
            if(delDir.delete()) bResult=true; else delDir.deleteOnExit(); 
            return bResult; 
            
		}
		
		public boolean delAll(String directory){
			// Deleted all tree directory
			    boolean result=false;
			File delDir=new File(directory);

			if(delDir.list().length>0){
			        String[] delList=delDir.list();
			        boolean flag=true;
			        for(int i=0; i<delList.length;i++){
			                String sFileFromList=directory+File.separator+delList[i];
			                File fileFromList= new File(sFileFromList);
			                if(fileFromList.isDirectory()){
			                        if(!delAll(sFileFromList)) flag=false;
			                }
			                else{
			                        if(!delFile(sFileFromList)) {
			                                flag= false;
			                                break;
			                        }
			                }
			        }
			        // flag = true if all files were deleted from directory
			        if(flag=true) result=delFile(directory);
			}
			else result=delFile(directory);

			return result;
		}

		public String getResult() {
	    	  // Deleted all tree directory
	          boolean result=false;
	          File delDir=new File(arg1);
	          if(delDir.isDirectory()) result= delAll(arg1); else result=delFile(arg1);
	          happendError=!result;
	          return "";
			}

	    public String getError() {
	    	  boolean bResult=true;
	          String result="";
	          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
	          if(numArg!=1) {result+="Bad format of command\n"; bResult=false;}
	          if(numArg==1) arg1=getAbsPath(workDir, arg1);
	          if(!pathCheck(arg1)) {result+="Directory not found\n"; bResult=false;}
	          happendError=!bResult;
	          return result;   
			}
	}
	
	public class Cd extends CommandInterface{
		public static  final String id="cd";
		public boolean happerdError=false;
		
	private boolean dirIsRoot(File fDir){
			 // Check if work directory is root.
            boolean bDirIsRoot=false;
            File[] arrayRoots=fDir.listRoots();
            for(int i=0; i<arrayRoots.length;i++){
                    if(arrayRoots[i].equals(fDir)) bDirIsRoot=true;
            } 
            
			return bDirIsRoot;
		}
    

	
	private class GoodPath{
		private String directory;
		private int numberPaths;
				
		//tets: f:\all\..\all\test\..\..
		public GoodPath(String dir) {
			directory=dir;
		}
		
		public String getResult(){
			String[] arrayDir=getArray();
			String root=arrayDir[numberPaths];
			String result="";
			boolean flag=true; // flag==true means that we cut one path with ".." //first value==true for start while
			
			while(flag){
				flag=false;
				for(int i=numberPaths; i>=0; i--){
					if(arrayDir[i].equals("..")) {
						cutPartArray(arrayDir, i);
						flag=true;
						break;
						} 
				}
			}
			if(numberPaths<0) result=root;
			
			for(int i=numberPaths; i>=0; i--){
				result+=arrayDir[i]+File.separator;
			}
			return result;
		} 
		
		public String[] getArray(){
			File fDir=new File(directory);
			String[] arrayDir=new String[directory.length()];
			int i=0;
			
			while(!dirIsRoot(fDir)){
				arrayDir[i]=fDir.getName();
				i++;
				fDir=fDir.getParentFile();
				if(fDir==null) break;
			}
			
			if(fDir!=null) {arrayDir[i]=fDir.toString();numberPaths=i;} else numberPaths=i-1; 
			
			return arrayDir;
		}
		
		private String[] cutPartArray(String[] arrayDir, int numUp)
			//cut path with number numUp and next path
			//numUp means path ".."
		{
			for(int i=numUp; i+2<=numberPaths; i++){
				arrayDir[i]=arrayDir[i+2];
			}
			numberPaths=numberPaths-2;
			return arrayDir;
		} 
	}
	
	public String getResult() throws DirectoryIsRootException, MyFileNotFoundException{
		String newDir=arg1;
  	  	String directory=workDir;
  	  
		GoodPath myDir=new GoodPath(newDir);
		String[] arrayPath= myDir.getArray();
		
		for(int i=myDir.numberPaths; i>=0; i--){
			directory=changeDir(directory, arrayPath[i]);			
		}
		
		//GoodPath myPath=new GoodPath(directory);
        //directory=myPath.getResult();
         
		workDir=directory;
		return directory;
	}
	
	public String changeDir(String directory, String newDir) throws DirectoryIsRootException, MyFileNotFoundException {
    	  File fDir=new File(directory);
          
          if(newDir.equals("..")){
                  if(!dirIsRoot(fDir)) directory=fDir.getParent(); else  throw new DirectoryIsRootException(); 
          }
          else{
                  File fArg=new File(newDir);
                  if(!fArg.exists())      newDir=directory+File.separator+newDir; 
                  File fNewDir=new File(newDir);
                  if(!fNewDir.exists()) throw new MyFileNotFoundException();
                  else directory = newDir;
          }
          
         
          
          return directory;
		}

      public String getError() {
    	  boolean bResult=true;
          String result="";
          if(workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(numArg!=1) {result+="Bad format of command\n"; bResult=false;}
          if(!pathCheck(workDir)) {result+="Directory not found\n"; bResult=false;}
          happendError=!bResult;
          return result;
		}
	}
	
	public class Mkdir extends CommandInterface{
		// Not work for ABSOLUT path!!! 
		public static  final String id="mkdir";
		public boolean happerdError=false;
        
      public String getResult() {
    	  String sNewDir=arg1;
    	  File newDir=new File(sNewDir);
          happendError= newDir.mkdirs();
          return "";
		}

      public String getError() {
    	  boolean bResult=true;
          String result="";
          if(Model.workDir.equals(null)) {result+="Null in direcory\n"; bResult=false;}
          if(!pathCheck(workDir)) {result+="Directory not found\n"; bResult=false;}
          if(numArg==1) arg1=workDir+File.separator+arg1; else {result+="Bad format of command\n"; bResult=false;}
          happendError=!bResult;
          return result;
		}
	}
	

	public class Exit extends CommandInterface{
		public static  final String id="exit";
		public boolean happerdError=false;
        
      public String getResult() {
    	  System.exit(0);
    	  return "CMD stop.";
		}

      public String getError() {
    	  return "";
		}
	}
	
	
	private boolean pathCheck(String directory){
        File f=new File(directory);
        return f.exists();
}
	private String getAbsPath(String directory, String arg){
        //translate name of file and work directory in absolute directory 
        File fArg=new File(arg);
        String absDir=arg;
        if(!fArg.exists()) absDir=directory+File.separator+arg;
        return absDir;
}

}
